
package com.idealighter.game.games.weishuwu.manager;

import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import com.idealighter.game.common.CommonMsgMgr;
import com.idealighter.game.core.common.Game;
import com.idealighter.game.core.constant.Operator;
import com.idealighter.game.core.error.ErrorCode;
import com.idealighter.game.dblog.service.DbLogService;
import com.idealighter.game.dictionary.dic.WeishuwuRoomDic;
import com.idealighter.game.dictionary.dic.WeishuwuTimeDic;
import com.idealighter.game.dictionary.domain.WeishuwuRoomDomain;
import com.idealighter.game.event.manager.EventMgr;
import com.idealighter.game.event.struct.ClearGameDataEvent;
import com.idealighter.game.event.struct.PlayerExitEvent;
import com.idealighter.game.event.struct.ShutdownRoomEvent;
import com.idealighter.game.event.struct.StartRoomEvent;
import com.idealighter.game.gamehall.event.EnterRoomEvent;
import com.idealighter.game.gamehall.event.EnterTableEvent;
import com.idealighter.game.gamehall.event.ExitRoomEvent;
import com.idealighter.game.gamehall.event.ExitTableEvent;
import com.idealighter.game.gamehall.event.RoomMemInfoUpdateEvent;
import com.idealighter.game.gamehall.manager.GameHallMgr;
import com.idealighter.game.games.common.AbstractRoom;
import com.idealighter.game.games.common.SeatState;
import com.idealighter.game.games.weishuwu.struct.GameStatus;
import com.idealighter.game.games.weishuwu.struct.WeishuwuRoom;
import com.idealighter.game.games.weishuwu.struct.WeishuwuSeat;
import com.idealighter.game.games.weishuwu.struct.WeishuwuTable;
import com.idealighter.game.log.core.LogReason;
import com.idealighter.game.log.struct.game.Game20WinLoseLog;
import com.idealighter.game.log.struct.game.GameAfeeLog;
import com.idealighter.game.login.manager.LoginMgr;
import com.idealighter.game.player.manager.PlayerMgr;
import com.idealighter.game.player.service.bo.PlayerBo;
import com.idealighter.game.player.struct.Player;
import com.idealighter.game.schedule.manager.ScheduleMgr;
import com.idealighter.game.server.ExecutorMgr;
import com.idealighter.game.server.core.executor.DisruptorExecutor;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// import com.idealighter.game.dblog.service.DbLogService;

/**
 * . 魏蜀吴逻辑处理 .
 * 
 * @date 2016年2月17日 上午10:46:55
 */
@Singleton
public class WeishuwuMgr {

  private static final Logger LOG = LoggerFactory.getLogger(WeishuwuMgr.class);

  // 玩家手上的牌数
  public static final int PLAYER_CARDS = 5;

  // 魏蜀吴定时服务器增加的延时，因为与网络延时等原因
  // private static final int ADDED_DELAY = 5;

  private static final long BANKER_NEED_CHIPS = 10000000;
  private final EventMgr eventMgr;
  // 游戏线程executor,可以执行普通任务和定时任务
  private final DisruptorExecutor gameExecutor;
  private final WeishuwuTimeDic timeDic;
  private final PlayerMgr playerMgr;
  private final ScheduleMgr scheduleMgr;
  private final CommonMsgMgr commonMsgMgr;
  private final WeishuwuMsgMgr msgMgr;
  private final WeishuwuDataMgr dataMgr;
  private final WeishuwuRoomDic roomDic;
  private final LoginMgr loginMgr;
  private final WeishuwuControlScript controlScript;

  /**
   * 构造函数 .
   * 
   * @param eventMgr .
   * @param executorMgr .
   * @param timeDic .
   * @param playerMgr .
   * @param scheduleMgr .
   * @param commonMsgMgr .
   * @param msgMgr .
   * @param dataMgr .
   * @param roomDic .
   * @param gameHallMgr .
   * @param loginMgr .
   * @param controlScript .
   */
  @Inject
  public WeishuwuMgr(EventMgr eventMgr, ExecutorMgr executorMgr, WeishuwuTimeDic timeDic,
      PlayerMgr playerMgr, ScheduleMgr scheduleMgr, CommonMsgMgr commonMsgMgr,
      WeishuwuMsgMgr msgMgr, WeishuwuDataMgr dataMgr, WeishuwuRoomDic roomDic,
      GameHallMgr gameHallMgr, LoginMgr loginMgr, WeishuwuControlScript controlScript) {
    this.playerMgr = playerMgr;
    this.scheduleMgr = scheduleMgr;
    this.commonMsgMgr = commonMsgMgr;
    this.msgMgr = msgMgr;
    this.dataMgr = dataMgr;
    this.roomDic = roomDic;
    this.loginMgr = loginMgr;
    this.eventMgr = eventMgr;
    this.timeDic = timeDic;
    this.controlScript = controlScript;
    eventMgr.register(this);
    this.gameExecutor = executorMgr.getGameExecutor(Game.WEISHUWU.getModuleId());
    gameStart();
  }

  /**
   * 订阅玩家退出游戏事件, 在gameExecutor中执行, 避免线程安全问题.
   * 
   * @param event .
   */
  @Subscribe
  public void onPlayerExitGame(PlayerExitEvent event) {
    gameExecutor.execute(() -> {
      Player player = event.player;
      long playerId = player.getId();

      WeishuwuSeat seat = dataMgr.getPlayerSeat(playerId);
      if (seat != null) {
        // 玩家在房间中但是没有准备，退出桌子，退出房间
        doExitTable(seat, player);
      }

      // 玩家是否在牛牛房间中
      if (player.curRoom instanceof WeishuwuRoom) {
        // 退出房间
        exitRoom(player);
      }
    });
  }

  /**
   * .
   * 
   * @param event .
   */
  @Subscribe
  public void onStartRoom(StartRoomEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.WEISHUWU) {
        roomDic.load();
      }
    });
  }

  /**
   * .
   * 
   * @param event .
   */
  @Subscribe
  public void onShutDownRoom(ShutdownRoomEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.WEISHUWU) {
        roomDic.load();
        AbstractRoom room = dataMgr.getRoom(event.getRoomId());
        for (Long playerId : room.getPlayers()) {
          Player player = playerMgr.getPlayer(playerId);
          if (player != null) {
            loginMgr.noticeLogoutForGameExit(player);
          }
        }
      }

    });
  }

  @Subscribe
  public void onClearGameData(ClearGameDataEvent event) {
    gameExecutor.execute(() -> doClearGameData(event.playerId));
  }

  /**
   * 进入游戏大厅 .
   * 
   * @param player 玩家.
   */
  public void enterGameHall(Player player) {
    if (player.curRoom != null) {
      Game game = player.curRoom.game();
      LOG.warn("[魏蜀吴]玩家[{}][{}]在[{}]游戏中，暂时无法进入游戏", player.getId(), player.getPlayerName(),
          game.getDesc());
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_ALREADY_IN, Operator.SYSTEM,
          game.getDesc());
      return;
    }

    msgMgr.sendEnterGameHallMsg(player);
    LOG.info("[魏蜀吴]玩家[{}][{}]进入大厅成功", player.getPlayerName(), player.getId());
  }

  /**
   * 玩家进入房间 .
   * 
   * @param player 玩家.
   * @param roomId .
   */
  public void enterRoom(Player player, int roomId) {
    AbstractRoom curRoom = player.curRoom;
    if (curRoom != null) {
      Game game = curRoom.game();
      LOG.warn("[魏蜀吴]玩家[{}][{}]当前在游戏[{}]房间[{}]中不能进入房间", player.getId(), player.getPlayerName(),
          game.getDesc(), curRoom.getId());
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_ALREADY_IN, Operator.SYSTEM,
          game.getDesc());
      return;
    }

    WeishuwuRoom room = dataMgr.getRoom(roomId);
    if (room == null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]进入的房间[{}]不存在", player.getPlayerName(), player.getId(), roomId);
      return;
    }

    WeishuwuRoomDomain roomDom = roomDic.get(roomId);
    if (roomDom.getIsActive() != 1) {
      LOG.error("[魏蜀吴]玩家[{}][{}]进入的房间[{}]已经关闭", player.getPlayerName(), player.getId(), roomId);
      return;
    }
    if (player.getGold() < roomDom.getLower()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.PLAYER_GOLD_NOT_ENOUGH, Operator.SYSTEM);
      LOG.warn("[魏蜀吴]玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(), player.getId(), roomId,
          roomDom.getLower());
      return;
    }
    if (player.getGold() > roomDom.getUpper()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.PLAYER_GOLD_BEYOND_MAX_GOLD, Operator.SYSTEM);
      LOG.warn("[魏蜀吴]玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(), player.getId(), roomId,
          roomDom.getUpper());
      return;
    }

    if (!player.vip()
        && room.getPlayers().size() >= roomDom.getMaxNum() * roomDom.getOrdinarPeople() / 100) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_NOT_VIP_ROOM_FULL, Operator.SYSTEM);
      LOG.warn("[魏蜀吴]玩家[{}][{}]进入的房间[{}]普通玩家人数已超过上限", player.getPlayerName(), player.getId(),
          roomId, roomDom.getMaxNum());
      return;
    }

    if (room.getPlayers().size() >= roomDom.getMaxNum()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_ROOM_FULL, Operator.SYSTEM);
      LOG.warn("[魏蜀吴]玩家[{}][{}]进入的房间[{}]已超过最大人数[{}]", player.getPlayerName(), player.getId(),
          roomId, roomDom.getMaxNum());
      return;
    }

    // 修改玩家当前房间
    player.curRoom = room;
    room.getPlayers().add(player.getId());
    // 发送进入房间消息
    msgMgr.sendEnterRoomMsg(player);
    LOG.info("[魏蜀吴]玩家[{}][{}]进入房间[{}]成功", player.getPlayerName(), player.getId(), roomId);

    // 触发进入房间事件
    eventMgr.post(new EnterRoomEvent(player, room));
  }

  /**
   * 兑换筹码的金币 玩家金币兑换筹码 .
   * 
   * @param player 玩家.
   * @param gold .
   */
  public void exchangeChips(Player player, long gold) {
    WeishuwuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]不在牌桌中不能兑换筹码", player.getPlayerName(), player.getId());
      return;
    }

    if (player.getGold() < gold
        + convertGold(seat.table.room.getId().intValue(), seat.getTotalChips())) {
      LOG.error("[魏蜀吴]玩家[{}][{}]兑换的金币[{}]加上筹码[{}]兑换的金币超过自己的金币总和[{}]", player.getPlayerName(),
          player.getId(), gold, seat.getTotalChips(), player.getGold());
      return;
    }

    WeishuwuTable table = seat.table;
    if (table.banker() == seat) {
      LOG.warn("[魏蜀吴]玩家[{}][{}]是庄家不允许上下分", player.getPlayerName(), player.getId());
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_BANKER_NOT_ALLOW_EXCHANGE,
          Operator.SYSTEM);
      return;
    }

    long chips = convertChip(seat.table.room.getId().intValue(), gold);
    seat.setTotalChips(seat.getTotalChips() + chips);
    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);

    LOG.info("[魏蜀吴]玩家[{}][{}]用金币[{}]兑换筹码[{}]成功", player.getPlayerName(), player.getId(), gold,
        chips);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));
  }

  /**
   * 玩家筹码兑换金币 .
   * 
   * @param player 玩家.
   * @param chips .
   */
  public void exchangeGold(Player player, long chips) {
    WeishuwuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]不在牌桌中不能兑换金币", player.getPlayerName(), player.getId());
      return;
    }

    if (seat.getTotalChips() < chips) {
      LOG.error("[魏蜀吴]玩家[{}][{}]兑换的筹码[{}]超过自己的筹码总和[{}]", player.getPlayerName(), player.getId(),
          chips, seat.getTotalChips());
      return;
    }
    WeishuwuTable table = seat.table;
    if (table.banker() == seat) {
      LOG.warn("[魏蜀吴]玩家[{}][{}]是庄家不允许上下分", player.getPlayerName(), player.getId());
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_BANKER_NOT_ALLOW_EXCHANGE,
          Operator.SYSTEM);
      return;
    } else {
      if (table.status != GameStatus.REST) {
        LOG.warn("[魏蜀吴]玩家[{}][{}]只有休息阶段才能下分", player.getPlayerName(), player.getId());
        commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_NOT_ALLOW_EXCHANGE, Operator.SYSTEM);
        return;
      }
    }

    seat.setTotalChips(seat.getTotalChips() - chips);
    long gold = convertGold(seat.table.room.getId().intValue(), chips);

    // 发送兑换筹码变更
    msgMgr.sendChipsChangeMsg(seat);

    LOG.info("[魏蜀吴]玩家[{}][{}]用筹码[{}]兑换金币[{}]成功", player.getPlayerName(), player.getId(), chips,
        gold);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));
  }

  /**
   * 玩家退出房间 .
   * 
   * @param player 玩家.
   */
  public void exitRoom(Player player) {
    AbstractRoom curRoom = player.curRoom;
    WeishuwuSeat seat = dataMgr.getPlayerSeat(player.getId());

    if (curRoom instanceof WeishuwuRoom) {
      if (seat == null) {
        player.curRoom = null;
        WeishuwuRoom room = (WeishuwuRoom) curRoom;
        room.getPlayers().remove(player.getId());
        msgMgr.sendExitRoomMsg(player, 0);

        LOG.info("[魏蜀吴]玩家[{}][{}]退出房间[{}]", player.getPlayerName(), player.getId(), room.getId());
        // 触发退出房间事件
        eventMgr.post(new ExitRoomEvent(player, curRoom));
      } else {
        LOG.error("[魏蜀吴]玩家[{}][{}]在座位中不能退出房间,请先退出桌子", player.getPlayerName(), player.getId());
        msgMgr.sendExitRoomMsg(player, 1);
      }
    } else {
      LOG.warn("[魏蜀吴]玩家[{}][{}]不在房间中，无法退出房间", player.getPlayerName(), player.getId());
    }
  }

  /**
   * 筹码换算金币,只会是多个金币等于一个筹码 .
   * 
   * @param roomId .
   * @param chips .
   */
  public long convertGold(int roomId, long chips) {
    WeishuwuRoomDomain roomDomain = roomDic.get(roomId);

    return chips * roomDomain.getProportionGold() / roomDomain.getProportionChips();
  }

  /**
   * 金币换算筹码 .
   * 
   * @param roomId .
   * @param gold .
   * @return
   */
  public long convertChip(int roomId, long gold) {
    WeishuwuRoomDomain roomDomain = roomDic.map().get(roomId);

    return gold * roomDomain.getProportionChips() / roomDomain.getProportionGold();
  }

  /**
   * 玩家进入牌桌 .
   * 
   * @param player 玩家.
   * @param tableId .
   * @param order .
   */
  public void enterTable(Player player, int tableId, byte order, String tablePwd) {
    if (!(player.curRoom instanceof WeishuwuRoom)) {
      LOG.error("[魏蜀吴]玩家[{}][{}]当前不在魏蜀吴房间中不能进入魏蜀吴桌子", player.getPlayerName(), player.getId());
      return;
    }

    WeishuwuTable table = dataMgr.getTable(tableId);

    if (table == null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]进入的牌桌[{}]不存在", player.getPlayerName(), player.getId(), tableId);
      return;
    }

    if (dataMgr.getPlayerSeat(player.getId()) != null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]已经在牌桌中不能重复进入牌桌", player.getPlayerName(), player.getId());
      return;
    }

    fastEnterTable(player, table.room.getId().intValue());

  }

  /**
   * 快速进入四人牛牛 .
   * 
   * @param player 玩家.
   * @param roomId .
   */
  public void fastEnterTable(Player player, int roomId) {
    if (!(player.curRoom instanceof WeishuwuRoom)) {
      LOG.error("[魏蜀吴]玩家[{}][{}]当前不在房间中不能直接进入桌子", player.getPlayerName(), player.getId());
      return;
    }

    WeishuwuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat != null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]已经在牌桌中不能重复进入牌桌", player.getPlayerName(), player.getId());
      return;
    }

    WeishuwuRoom room = dataMgr.getRoom(roomId);
    if (room == null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]快速进入的房间[{}]不存在", player.getPlayerName(), player.getId(), roomId);
      return;
    }

    WeishuwuSeat emptySeat = findEmptySeat(player, room);
    if (emptySeat != null) {
      doEnterTable(player, emptySeat, null);
    } else {
      // 发送快速进入桌子结果
      msgMgr.sendFastEnterTableMsg(player, 1);
      commonMsgMgr.sendTipDlgMsg(player, ErrorCode.GAME_NOT_SIT, Operator.SYSTEM);
    }
  }

  /**
   * 玩家进入座位 .
   * 
   * @param player 玩家.
   * @param seat 座位信息.
   */
  private void doEnterTable(Player player, WeishuwuSeat seat, String tablePwd) {
    int roomId = seat.table.room.getId().intValue();
    WeishuwuTable table = seat.table;

    if (table.readied) {
      LOG.warn("[魏蜀吴]玩家[{}][{}]进入的桌子[{}]已经开始游戏", player.getPlayerName(), player.getId(),
          table.getId());
      return;
    }

    WeishuwuRoomDomain roomDom = roomDic.get(seat.table.room.getId().intValue());
    if (player.getGold() < roomDom.getLower()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.PLAYER_GOLD_NOT_ENOUGH, Operator.SYSTEM);
      LOG.warn("[魏蜀吴]玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(), player.getId(), roomId,
          roomDom.getLower());
      return;
    }

    if (player.getGold() > roomDom.getUpper()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.PLAYER_GOLD_BEYOND_MAX_GOLD, Operator.SYSTEM);
      LOG.warn("[魏蜀吴]玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(), player.getId(), roomId,
          roomDom.getUpper());
      return;
    }

    if (player.getGold() < roomDom.getAfee()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_NOT_ENOUGH_FEE, Operator.SYSTEM);
      LOG.warn("[魏蜀吴]玩家[{}][{}]进入桌子金币不足台费", player.getPlayerName(), player.getId());
      return;
    } else {
      playerMgr.minusGold(player, roomDom.getAfee(), LogReason.WEISHUWU_ENTER_TABLE_MINUS_AFEE);
      DbLogService.log(new GameAfeeLog(player, table, roomDom.getAfee()));
    }

    seat.setPlayerId(player.getId());
    seat.setPlayerName(player.getPlayerName());
    seat.setSex(player.getSex());
    seat.setRobot(player.robot());
    seat.setState(SeatState.GAMING);

    // 更新玩家座位
    dataMgr.updatePlayerSeats(player.getId(), seat);
    // 触发进入桌子事件
    eventMgr.post(new EnterTableEvent(player, seat));

    // 向玩家发送进入牌桌结果信息
    int time = (int) table.stepFuture.getDelay(TimeUnit.SECONDS);
    if (time < 0) {
      doBet(table);
      time = timeDic.get(GameStatus.BET.val).getTime();
    }
    msgMgr.sendEnterTableMsg(player, table);
    msgMgr.sendOtherEnterTableMsg(seat);
    msgMgr.sendHistory(seat);
    msgMgr.sendTableBetInfosMsg(seat);
    msgMgr.sendApplyBankersMsg(seat);
    msgMgr.sendBankerInfoMsg(seat);
    msgMgr.sendGameStage(seat, time < 0 ? 5 : time);
    LOG.info("[魏蜀吴]玩家[{}][{}]进入桌子[{}]座位[{}]成功", player.getPlayerName(), player.getId(),
        table.getId(), seat.getOrder());
  }

  /**
   * 玩家退出牌桌 .
   * 
   * @param player 玩家.
   */
  public void exitTable(Player player) {
    WeishuwuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]没在牌桌中不能退出牌桌", player.getPlayerName(), player.getId());
      return;
    }

    // 退出桌子
    doExitTable(seat, player);
  }

  /**
   * 退出桌子 .
   * <p/>
   * 1.玩家正常退出牌桌, 能在在线玩家中拿到player, 正常退出桌子
   * <p/>
   * 2.玩家非正常退出(如掉线)后,从活跃玩家或数据库中拿到PlayerDomain，结算后马上更新缓冲和数据库
   * 
   * @param seat 座位信息.
   * @param player 玩家.
   */
  private void doExitTable(WeishuwuSeat seat, Player player) {
    // long playerId = seat.playerId;
    // Player player = playerMgr.getPlayer(playerId);
    WeishuwuTable table = seat.table;

    // 发送退出房间消息
    if (table.status == GameStatus.REWARD) { // 开奖过程中退出，立刻更新玩家金币
      playerMgr.noticeGold(playerMgr.getPlayer(seat.getPlayerId()));
    } else if (table.status == GameStatus.BET) { // 下注退,退还下注金币
      // playerMgr.addGold(player, convertGold(table.room.id,
      // seat.totalBets()), LogReason.WEISHUWU_BILL);
    }

    if (seat == table.banker()) { // 庄家退
      table.bankerId = 0;
      if (table.status == GameStatus.REST) {
        doChangeBanker(table);
      }
    } else {
      table.applyBankers.remove(seat.getPlayerId());
      msgMgr.sendApplyBankersMsg(table);
    }

    msgMgr.sendExitTableMsg(seat);
    dataMgr.removePlayerSeat(seat.getPlayerId());
    LOG.info("[魏蜀吴]玩家[{}][{}]退出桌子[{}]", seat.getPlayerId(), seat.getPlayerName(),
        seat.table.getId());
    seat.clear();
    if (table.status == GameStatus.BET) { // 下注阶段退同步一次筹码
      msgMgr.sendTableBetInfosMsg(table);
    }

    // 触发退出桌子事件
    eventMgr.post(new ExitTableEvent(player, seat));
  }

  /**
   * 开始游戏 .
   */
  private void gameStart() {
    for (WeishuwuRoom room : dataMgr.rooms()) {
      for (WeishuwuTable table : room.tables()) {
        doRest(table);
      }
    }
  }

  /**
   * 休息阶段 .
   * 
   * @param table .
   */

  private void doRest(WeishuwuTable table) {
    table.status = GameStatus.REST;
    int time = timeDic.get(table.status.val).getTime();
    // bankerNotice(table);
    table.banker().resetGameData();
    for (WeishuwuSeat seat : table.seats) {
      if (seat.getPlayerId() > 0) {
        playerMgr.noticeGold(playerMgr.getPlayer(seat.getPlayerId()));
      }
      seat.areaBill.clear();
      seat.resetGameData();
    }
    msgMgr.sendGameStage(table, time);
    // 改变庄家
    doChangeBanker(table);
    table.reset();
    table.stepFuture =
        scheduleMgr.schedule(() -> doBet(table), time, TimeUnit.SECONDS, gameExecutor);
    LOG.info("[魏蜀吴]房间[{}]桌子[{}]阶段[{}]", table.room.getId(), table.getId(), table.status.desc);
  }

  /**
   * 下注阶段 .
   * 
   * @param table .
   */
  private void doBet(WeishuwuTable table) {
    table.status = GameStatus.BET;
    int time = timeDic.get(table.status.val).getTime();
    msgMgr.sendGameStage(table, time);
    table.stepFuture =
        scheduleMgr.schedule(() -> doReward(table), time, TimeUnit.SECONDS, gameExecutor);
    LOG.info("[魏蜀吴]房间[{}]桌子[{}]阶段[{}]", table.room.getId(), table.getId(), table.status.desc);
  }

  /**
   * 开奖阶段 .
   * 
   * @param table .
   */
  private void doReward(WeishuwuTable table) {
    table.status = GameStatus.REWARD;
    int time = timeDic.get(table.status.val).getTime();
    msgMgr.sendGameStage(table, time);
    // controlScript.dealCards(table);
    controlScript.controlDealCards(table);
    table.deduceWinner();
    // balanceTable(table);
    table.balance();
    controlScript.balanceControl(table);
    msgMgr.sendDealCardsMsg(table);
    msgMgr.sendRewardResult(table);
    table.addHistory();
    msgMgr.sendHistory(table);
    dobalance(table);
    table.stepFuture =
        scheduleMgr.schedule(() -> doRest(table), time, TimeUnit.SECONDS, gameExecutor);
    LOG.info("[魏蜀吴]房间[{}]桌子[{}]阶段[{}]", table.room.getId(), table.getId(), table.status.desc);
  }

  /**
   * 结算阶段 .
   * 
   * @param table .
   */
  private void dobalance(WeishuwuTable table) {
    WeishuwuSeat banker = table.banker();
    banker.bankerNum++;
    banker.bankerScore += banker.billChips;
    for (WeishuwuSeat seat : table.seats) {
      if (seat.getPlayerId() > 0) {
        doBill(seat);
      }
    }

    // table.banker().resetGameData();
    // 结算后，如果庄家是玩家，校验钱够不够
    if (banker.getPlayerId() > 0 && banker.getTotalChips() < BANKER_NEED_CHIPS) {
      table.bankerId = 0;
      doChangeBanker(table);
    }

    // msgMgr.sendGameStage(table, time);
    // 同步庄家信息
    msgMgr.sendBankerInfoMsg(table);
    // 结算消息

    //
    // table.stepFuture = scheduleMgr.schedule(() -> doRest(table), time,
    // TimeUnit.SECONDS, gameExecutor);
    // LOG.info("魏蜀吴房间[{}]桌子[{}]阶段[{}]", table.room.id, table.id,
    // table.status.desc);

  }

  /**
   * 查找空位置, 优先查找有人的桌子.
   * 
   * @param player 玩家.
   * @param room .
   * @return
   */
  private WeishuwuSeat findEmptySeat(Player player, WeishuwuRoom room) {
    WeishuwuSeat emptySeat = null;
    ArrayList<WeishuwuTable> tables = new ArrayList<>(room.tables.values());
    Collections.shuffle(tables);

    for (WeishuwuTable table : tables) {
      for (WeishuwuSeat seat : table.seats) {
        if (seat.getPlayerId() == 0) {
          emptySeat = seat;
        }
      }
    }

    return emptySeat;
  }

  /**
   * 玩家下注 .
   * 
   * @param player 玩家.
   * @param area .
   * @param chips .
   */
  public void playerBet(Player player, int area, long chips) {
    WeishuwuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (area < 0 || area > 8) {
      LOG.error("[魏蜀吴]玩家[{}][{}]下注区域[{}]不合法", player.getUserName(), player.getId(), area);
      return;
    }
    if (seat == null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]不在牌桌中不能下注", player.getPlayerName(), player.getId());
      return;
    }
    if (seat.table.status != GameStatus.BET) {
      LOG.error("[魏蜀吴]非下注阶段玩家[{}][{}]不能下注", player.getPlayerName(), player.getId());
      return;
    }
    long areaBet = seat.bets.getOrDefault(area, 0L) + chips;
    // long limitBet = seat.totalBets() + chips;

    if (chips > seat.getTotalChips()) {
      LOG.error("[魏蜀吴]玩家[{}][{}]下注总和[{}]不能超过自己的筹码[{}]", player.getPlayerName(), player.getId(),
          chips, seat.getTotalChips());
      return;
    }
    WeishuwuSeat banker = seat.table.banker();
    if (banker.getPlayerId() != 0) {
      if (chips * seat.table.cbMultiple[area] + seat.table.limitChips(area) > banker
          .getTotalChips()) {
        LOG.error("[魏蜀吴]玩家[{}][{}]下注失败[{}]下注额度已超过庄家最大筹码数[{}]", player.getPlayerName(),
            player.getId(), chips, banker.getTotalChips());
        // commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_BANKER_CHIP_NOT_ENOUGH,
        // Operator.SYSTEM);
        return;
      }
    }
    seat.bets.put(area, areaBet);
    Map<Integer, Long> tableBets = seat.table.tableBets;
    long tableBet = tableBets.getOrDefault(area, 0L);
    tableBets.put(area, tableBet + chips);

    /*
     * 扣除筹码和金币 .
     */
    seat.setTotalChips(seat.getTotalChips() - chips);
    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);
    playerMgr.minusGold(player, convertGold(seat.table.room.getId().intValue(), chips),
        LogReason.WEISHUWU_BET);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));

    // 发送下注成功
    msgMgr.sendBetMsg(player, area, chips);
    // 同步桌子下注筹码消息
    msgMgr.sendTableBetInfosMsg(seat.table);

    LOG.info("[魏蜀吴]玩家[{}][{}]下注区域[{}][{}]成功", player.getPlayerName(), player.getId(), area, chips);
  }

  /**
   * 清除下注 .
   * 
   * @param player 玩家.
   */
  public void clearBet(Player player) {
    WeishuwuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]没有在桌子中不能清除下注筹码", player.getPlayerName(), player.getId());
      return;
    }

    WeishuwuTable table = seat.table;
    if (table.status != GameStatus.BET) {
      LOG.error("[魏蜀吴]玩家[{}][{}]不是下注阶段不能清除下注", player.getPlayerName(), player.getId());
      msgMgr.sendClearBetMsg(player, 1);
      return;
    }

    // 牌桌合计下注
    Map<Integer, Long> tableBets = table.tableBets;
    // 合计下注
    final long totalBets = seat.totalBets();
    Map<Integer, Long> bets = seat.bets;
    for (Entry<Integer, Long> betEtr : bets.entrySet()) {
      int area = betEtr.getKey();
      long tableBet = tableBets.getOrDefault(area, 0L);
      tableBets.put(area, tableBet - betEtr.getValue());
    }

    seat.bets.clear();
    msgMgr.sendClearBetMsg(player, 0);

    seat.setTotalChips(seat.getTotalChips() + totalBets);
    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);
    playerMgr.addGold(player, convertGold(seat.table.room.getId().intValue(), totalBets),
        LogReason.WEISHUWU_CLEAR_BET);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));

    msgMgr.sendTableBetInfosMsg(seat.table);
    LOG.info("[魏蜀吴]玩家[{}][{}]清除下注成功", player.getPlayerName(), player.getId());
  }

  /**
   * 申请庄家 .
   * 
   * @param player 玩家.
   */
  public void applyBanker(Player player) {
    WeishuwuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]没有在桌子中不能申请庄家", player.getPlayerName(), player.getId());
      return;
    }
    WeishuwuTable table = seat.table;
    if (seat.getPlayerId() == table.bankerId) {
      LOG.info("[魏蜀吴]玩家[{}][{}]已经是庄家申请失败", player.getPlayerName(), player.getId());
      return;
    }
    if (seat.getTotalChips() < BANKER_NEED_CHIPS) {
      LOG.error("[魏蜀吴]玩家[{}][{}]不够庄家最小筹码数[{}]申请失败", player.getPlayerName(), player.getId(),
          BANKER_NEED_CHIPS);
      commonMsgMgr.sendErrorDlgMsg(playerMgr.getPlayer(seat.getPlayerId()),
          ErrorCode.GAME_CHIP_NOT_ENOUGH, Operator.SYSTEM, BANKER_NEED_CHIPS + "");
      // commonMsgMgr.sendErrorDlgMsg(playerMgr.getPlayer(seat.playerId),
      // 80, 0);
      return;
    }

    if (table.applyBankers.size() >= WeishuwuTable.MAX_BANKER_SIZE) {
      LOG.warn("[魏蜀吴]玩家[{}][{}]申请庄家时人数超过上限[{}]", player.getPlayerName(), player.getId(),
          WeishuwuTable.MAX_BANKER_SIZE);
      commonMsgMgr.sendTipDlgMsg(player, ErrorCode.GAME_BANKER_QUEUE_FULL, Operator.SYSTEM);
      return;
    } else {
      if (table.applyBankers.contains(player.getId())) {
        LOG.error("[魏蜀吴]玩家[{}][{}]已经在庄家列表中!,申请失败！");
        return;
      }
      table.applyBankers.add(player.getId());
      msgMgr.sendApplyBankersMsg(table);
      if (table.status == GameStatus.REST) {
        doChangeBanker(table);
      }
    }
    LOG.info("[魏蜀吴]玩家[{}][{}]申请庄家成功", player.getPlayerName(), player.getId());
  }

  /**
   * 玩家下庄 .
   * 
   * @param player 玩家.
   */
  public void offBanker(Player player) {
    WeishuwuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]没有在桌子中不能下庄", player.getPlayerName(), player.getId());
      return;
    }
    WeishuwuSeat banker = seat.table.banker();
    if (banker != seat) {
      LOG.error("[魏蜀吴]玩家[{}][{}]不是庄家不能下庄", player.getPlayerName(), player.getId());
      return;
    }

    WeishuwuTable table = seat.table;
    if (table.status != GameStatus.REST) {
      LOG.error("[魏蜀吴]玩家[{}][{}]只有在休息阶段才能下庄", player.getPlayerName(), player.getId());
      return;
    }

    banker.bankerNum = 0;
    banker.bankerScore = 0;
    table.bankerId = 0;
    msgMgr.sendBankerInfoMsg(table);
    doChangeBanker(table);

    LOG.info("[魏蜀吴]玩家[{}][{}]下庄成功", player.getPlayerName(), player.getId());
  }

  /**
   * 取消申请庄家 .
   * 
   * @param player 玩家.
   */
  public void cancelApplyBanker(Player player) {
    WeishuwuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[魏蜀吴]玩家[{}][{}]没有在桌子中不能申请庄家", player.getPlayerName(), player.getId());
      return;
    }

    WeishuwuTable table = seat.table;
    if (table.applyBankers.contains(player.getId())) {
      table.applyBankers.remove(player.getId());
      msgMgr.sendApplyBankersMsg(table);
    }

    LOG.info("[魏蜀吴]玩家[{}][{}]取消申请庄家成功", player.getPlayerName(), player.getId());
  }

  /**
   * 换庄家 .
   * 
   * @param table .
   */
  private void doChangeBanker(WeishuwuTable table) {
    // boolean flag = false;
    checkApplayBanker(table);
    if (table.bankerId == 0) {
      Long applicant = null;
      while ((applicant = table.applyBankers.poll()) != null) {
        WeishuwuSeat seat = dataMgr.getPlayerSeat(applicant);
        if (seat.getTotalChips() >= BANKER_NEED_CHIPS) {
          table.bankerId = applicant;
          // 换庄后重置系统位置数据
          // table.sysSeat.reset();
          table.dealer().reset();
          msgMgr.sendApplyBankersMsg(table);
          msgMgr.sendBankerInfoMsg(table);
          // flag = true;
          return;
        }
      }
    }
    msgMgr.sendApplyBankersMsg(table);
    msgMgr.sendBankerInfoMsg(table);

  }

  private void checkApplayBanker(WeishuwuTable table) {
    List<Long> list = new ArrayList<>();
    list.addAll(table.applyBankers);
    for (Long playerId : list) {
      WeishuwuSeat seat = dataMgr.getPlayerSeat(playerId);
      if (seat == null) {
        continue;
      }
      if (seat.getTotalChips() < BANKER_NEED_CHIPS) {
        table.applyBankers.remove(playerId);
        msgMgr.sendApplyBankersMsg(table);
        commonMsgMgr.sendTipDlgMsg(playerMgr.getPlayer(seat.getPlayerId()),
            ErrorCode.GAME_CHIP_NOT_ENOUGH, Operator.SYSTEM);
      }
    }
  }

  /**
   * 座位结算 .
   * 
   * @param seat 座位信息.
   */
  private void doBill(WeishuwuSeat seat) {
    long playerId = seat.getPlayerId();
    int roomId = seat.table.room.getId().intValue();
    // 扣除输赢
    seat.setTotalChips(seat.getTotalChips() + seat.billChips);
    long billGold = convertGold(roomId, seat.billChips);

    /*
     * 游戏结算金币 .
     */
    Player player = playerMgr.getPlayer(playerId);
    PlayerBo playerDom = playerMgr.selectPlayer(playerId);
    if (player != null) {
      // playerMgr.addGold(player, billGold, false,
      // LogReason.WEISHUWU_BILL);
      playerMgr.addGold(player, billGold, true, false, LogReason.WEISHUWU_BILL);
    } else {
      playerMgr.addGold(playerDom, billGold, LogReason.WEISHUWU_BILL);
    }

    LOG.info("[魏蜀吴]玩家[{}][{}]结算筹码[{}]", player.getPlayerName(), player.getId(), seat.billChips);

    DbLogService.log(
        new Game20WinLoseLog(playerDom, seat, seat.billChips, billGold, LogReason.WEISHUWU_BILL));

    msgMgr.sendBanlance(seat);
    // seat.resetGameData();
    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);
    // eventMgr.post(new
    // RoomMemInfoUpdateEvent(playerMgr.getPlayer(playerId)));
  }

  /**
   * 清除玩家,服务器出现bug，玩家卡在游戏中，后台可以清除玩家 .
   * 
   * @param playerId 玩家id.
   */
  private void doClearGameData(long playerId) {
    try {
      dataMgr.removePlayerSeat(playerId);
      /*
       * 清除玩家房间数据 .
       */
      for (WeishuwuRoom room : dataMgr.rooms()) {
        room.getPlayers().remove(playerId);
        /*
         * 清除房间中玩家所在桌子数据 .
         */
        for (WeishuwuTable table : room.tables.values()) {
          table.applyBankers.remove(playerId);
          if (table.bankerId == playerId) {
            table.bankerId = 0;
          }
          for (WeishuwuSeat seat : table.seats) {
            if (seat.getPlayerId() == playerId) {
              seat.clear();
              Player player = playerMgr.getPlayer(playerId);
              if (player != null) {
                loginMgr.noticeLogoutForGameExit(player);
              }
            }
          }
        }
      }

      Player player = playerMgr.getPlayer(playerId);
      if (player != null) {
        loginMgr.noticeLogoutForGameExit(player);
      }
    } catch (Exception e) {
      LOG.error("[魏蜀吴]清除玩家[" + playerId + "]卡限失败", e);
    }
  }
}
